package com.swotxu.oldwebproject.session.handler;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.NamedThreadLocal;
import org.springframework.util.CollectionUtils;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * 用于缓存一次请求中，redis session中的 key-value。
 *
 * redis session改造前：应用层获取session中的引用对象，直接修改对象属性，对session可见。
 * redis session改造后：应用层修改session中的对象，对redis不可见。
 *
 * 现当应用层每次调用 session.getAttribute() 时，检查线程缓存是否存在指定 key，
 * 若不存在，则将当前的 key-value 缓存至 ThreadLocal中，并返回给应用层，
 * 若存在，则直接返回当前缓存中的 key-value。
 *
 * @Date: 2020/1/13 14:43
 * @Author: xu.hai
 */
public class SessionAttributeHandler {
    private static final Logger LOGGER = LoggerFactory.getLogger(RedisSessionHandler.class);
    private static final ThreadLocal<Map<String, Map<String, Object>>> ATTR_CACHE;

    static {
        ATTR_CACHE = new NamedThreadLocal<Map<String, Map<String, Object>>>("SessionAttributeThreadLocal"){
            @Override
            protected Map<String, Map<String, Object>> initialValue() {
                return new HashMap<>();
            }
        };
    }

    public static void hset(String sessionId, String name, Object value){
        LOGGER.debug("SessionThreadLocal hset -> sessionId: {}, name: {}, value: {}", sessionId, name, value);
        Map<String, Map<String, Object>> map = ATTR_CACHE.get();
        Map<String, Object> fieldMap = map.get(sessionId);
        if(CollectionUtils.isEmpty(fieldMap)){
            fieldMap = new HashMap();
            map.put(sessionId, fieldMap);
        }
        map.get(sessionId).put(name, value);
    }

    public static Object hget(String sessionId, String name){
        LOGGER.debug("SessionThreadLocal hget -> sessionId: {}, name: {}", sessionId, name);
        return hexists(sessionId, name)? ATTR_CACHE.get().get(sessionId).get(name) : null;
    }

    public static boolean hexists(String sessionId, String name){
        LOGGER.debug("SessionThreadLocal hexists -> sessionId: {}, name: {}", sessionId, name);
        Map<String, Map<String, Object>> map = ATTR_CACHE.get();
        return map.containsKey(sessionId) && map.get(sessionId).containsKey(name);
    }

    public static Set<String> getKeys(){
        return ATTR_CACHE.get().keySet();
    }

    public static Map<String, Object> getFieldMap(String sessionId){
        LOGGER.debug("SessionThreadLocal getFieldMap -> sessionId: {}, name: {}", sessionId);
        return ATTR_CACHE.get().containsKey(sessionId)?
                Collections.unmodifiableMap(ATTR_CACHE.get().get(sessionId)) : null;
    }

    public static void removeAll(){
        ATTR_CACHE.remove();
    }

    public static void removeField(String sessionId, String name){
        LOGGER.debug("SessionThreadLocal removeField -> sessionId: {}, name: {}", sessionId, name);
        if(hexists(sessionId, name)){
            ATTR_CACHE.get().remove(name);
        }
    }

}
